using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using HuskyRescue.Core.ViewModel.Accounts.Identity;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;

namespace HuskyRescue.Core.Service.Accounts
{
	// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
	public class ApplicationUserManager : UserManager<ApplicationUser>
	{
		public ApplicationUserManager(IUserStore<ApplicationUser> store)
			: base(store)
		{
		}

		public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
		{
			var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
			// Configure validation logic for usernames
			manager.UserValidator = new UserValidator<ApplicationUser>(manager)
			                        {
				                        AllowOnlyAlphanumericUserNames = false,
				                        RequireUniqueEmail = true
			                        };
			// Configure validation logic for passwords
			manager.PasswordValidator = new PasswordValidator
			                            {
				                            RequiredLength = 10,
				                            RequireNonLetterOrDigit = false,
				                            RequireDigit = false,
				                            RequireLowercase = true,
				                            RequireUppercase = true,
			                            };
			// Configure user lockout defaults
			manager.UserLockoutEnabledByDefault = true;
			manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
			manager.MaxFailedAccessAttemptsBeforeLockout = 5;
			// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
			// You can write your own provider and plug in here.
			manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
			                                               {
				                                               MessageFormat = "Your security code is: {0}"
			                                               });
			manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
			                                               {
				                                               Subject = "SecurityCode",
				                                               BodyFormat = "Your security code is {0}"
			                                               });
			manager.EmailService = new EmailService();
			manager.SmsService = new SmsService();
			var dataProtectionProvider = options.DataProtectionProvider;
			if (dataProtectionProvider != null)
			{
				manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
			}
			return manager;
		}

		/// <summary>
		/// Method to add user to multiple roles
		/// </summary>
		/// <param name="userId">user id</param>
		/// <param name="roles">list of role names</param>
		/// <returns></returns>
		public virtual async Task<IdentityResult> AddUserToRolesAsync(string userId, IList<string> roles)
		{
			var userRoleStore = (IUserRoleStore<ApplicationUser, string>)Store;

			var user = await FindByIdAsync(userId).ConfigureAwait(false);
			if (user == null)
			{
				throw new InvalidOperationException("Invalid user Id");
			}

			var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
			// Add user to each role using UserRoleStore
			foreach (var role in roles.Where(role => !userRoles.Contains(role)))
			{
				await userRoleStore.AddToRoleAsync(user, role).ConfigureAwait(false);
			}

			// Call update once when all roles are added
			return await UpdateAsync(user).ConfigureAwait(false);
		}

		/// <summary>
		/// Remove user from multiple roles
		/// </summary>
		/// <param name="userId">user id</param>
		/// <param name="roles">list of role names</param>
		/// <returns></returns>
		public virtual async Task<IdentityResult> RemoveUserFromRolesAsync(string userId, IList<string> roles)
		{
			var userRoleStore = (IUserRoleStore<ApplicationUser, string>)Store;

			var user = await FindByIdAsync(userId).ConfigureAwait(false);
			if (user == null)
			{
				throw new InvalidOperationException("Invalid user Id");
			}

			var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
			// Remove user to each role using UserRoleStore
			foreach (var role in roles.Where(userRoles.Contains))
			{
				await userRoleStore.RemoveFromRoleAsync(user, role).ConfigureAwait(false);
			}

			// Call update once when all roles are removed
			return await UpdateAsync(user).ConfigureAwait(false);
		}
	}
}